home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
tchasm.arc
/
TCHASM.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1986-01-26
|
45KB
|
1,847 lines
{$C-,I-,K-}
Program TChasm;
{**** Here are the hooks into the procedures for the Editor Toolbox ****}
{$I VARS.ED } { Toolbox global variables and data structure definitions }
procedure UserCommand(var ch : byte);
{ user command processor hook }
begin
end;
procedure UserError(var Msgno : byte);
{ user error handler hook }
begin
end;
procedure userStatusline(Var TWindow:Byte;
Column,line:Integer;
Insertflag:Insflag;
WW,AI: boolean);
{ user status line handler }
begin
end;
procedure UserReplace(var ch : byte);
{ user replace handler hook }
begin
end;
procedure UserTask;
{ user multi-tasking hook }
begin
end;
{$I USER.ED } { Editor kernel and primitive level helper routines }
{$I SCREEN.ED } { Screen updating routines }
{$I INIT.ED } { initialization code }
{$I KCMD.ED } { Ctrl-K routines }
{$I QCMD.ED } { Ctrl-Q routines }
{$I CMD.ED } { general editing commands }
{$I K.ED } { Ctrl-K dispatcher and interface }
{$I Q.ED } { Ctrl-Q dispatcher and interface }
{$I DISP.ED } { General command dispatcher }
{$I TASK.ED } { Scheduling subsystem and central dispatcher }
{$I INPUT.ED } { Input routines }
Function Exist(FileN: AnyString): boolean; {Checks to see if file exists }
var F: file;
begin
{$I-}
assign(F,FileN);
reset(F);
{$I+}
if IOResult<>0 then Exist:=false
else
begin
Exist:=true;
close(F);
end
end;
Procedure LowVideo;
begin
TextColor(BlockColor);
end;
Procedure NormVideo;
begin
TextColor(TxtColor);
end;
Procedure GetLine; {Gets next line of Source from either memory or from
disk, depending on SourceLoc which may be the disk if there wasn't enough
memory for it in the editor. I think this would be the place to install
the hooks for a macro processor }
var
L : integer;
Begin {GetLine }
Case SourceLoc of
Disk : If NOT EOF(SourceFile) then
Readln(SourceFile,InpLine)
Else
EndOfSource := true;
Memory : With Curwin^ Do
If CurLine <> NIL then
begin
L := CurLine^.BuffLen; {Set Length of line }
Move(CurLine^.Txt^[1],InpLine[1],L); {and trim its end }
While (L > 0) AND (InpLine[L] = ' ') Do L := Pred(L);
InpLine[0] := Chr(L);
CurLine := CurLine^.FwdLink; {move forward for }
end {next line }
Else
EndOfSource := true;
End; {Case SourceLoc }
LineNum := Succ(LineNum); {set various assembler vars }
NeedOffset := NONE;
DSFlag := false;
ObjLen := 0;
End; {GetLine }
{***** NextWord returns tokens or words separated by delimiters that *****
separate them, such as commas or spaces, sent as [' ',','], etc.
starting at StartPos. Is included only for reading Tchasm.Dat }
Procedure NextWord(Line : AnyString; var Word : AnyString;
ParsePos : byte ; DelimSet : SetOfChar);
var
StartPos : byte;
Begin {NextWord }
{Skip any leading characters that aren't wanted }
While (Line[ParsePos] in DelimSet) AND (ParsePos < Length(Line)) Do
ParsePos := Succ(ParsePos);
StartPos := ParsePos;
While NOT (Line[ParsePos] in DelimSet) AND (ParsePos <= Length(Line)) DO
ParsePos := Succ(ParsePos); {move one past token }
Word := Copy(Line,StartPos,ParsePos-StartPos);
ParsePos := Succ(ParsePos);
End; {NextWord }
Procedure ErrorMessage(ErrMsg : AnyString); {Assembler error messages }
Begin
If ListLoc <> NoIO then
Writeln(ListFile,'** Error: ',ErrMsg,' ** ',LineNum);
Errs := Succ(Errs);
End;
Procedure DiagMessage(DiagMsg : AnyString); {Assembler Diagnostics }
Begin
If ListLoc <> NoIO then
Writeln(ListFile,'** Diagnostic: ',DiagMsg,' ** ',LineNum);
Diag := Succ(Diag);
End;
Function Hex(Num : integer) : AnyString; {same as ConvertBase, but takes
number rather than string }
CONST
R1 = 10; {base to convert from }
R2 = 16; {base to convert to }
var
V,V2,T : Real;
C : byte;
TempHex : AnyString;
Begin {Hex }
Str(Num,TempHex);
T := 0;
For C := 1 to Length(TempHex) Do
begin
V := Ord(TempHex[C]);
If (V > 47) AND (V < 58) then V2 := V - 48;
If (V > 64) AND (V < 91) then V2 := V - 55;
If (V > 96) AND (V < 123) then V2 := V - 85;
T := T * R1 + V2;
end;
TempHex := ''; {Don't need it anymore }
While T <> 0 Do
begin
V2 := T - Trunc(T/R2)*R2;
T := (T - V2)/R2;
If V2 < 10 then V := V2 + 48;
If V2 > 9 then V := V2 + 55;
TempHex := Chr(Round(V)) + TempHex;
end;
If Length(TempHex) = 0 then TempHex := '0';
Hex := TempHex;
End; {Hex }
Function ConvertBase(Num : AnyString; FrBase,ToBase : NumTypes) : AnyString;
{Converts numbers from FrBase to ToBase }
var
V,V2,T : Real;
C,R1,R2 : byte; {R2 is base to convert to }
Begin {ConvertBase }
Case FrBase of
Hexadecimal : R1 := 16;
BaseTen : R1 := 10;
Binary : R1 := 2;
End; {Case }
Case ToBase of
Hexadecimal : R2 := 16;
BaseTen : R2 := 10;
Binary : R2 := 2;
End; {Case }
T := 0;
For C := 1 to Length(Num) Do
begin
V := Ord(Num[C]);
If (V > 47) AND (V < 58) then V2 := V - 48;
If (V > 64) AND (V < 91) then V2 := V - 55;
If (V > 96) AND (V < 123) then V2 := V - 85;
T := T * R1 + V2;
end;
Num := ''; {Don't need it anymore }
While T <> 0 Do
begin
V2 := T - Trunc(T/R2)*R2;
T := (T - V2)/R2;
If V2 < 10 then V := V2 + 48;
If V2 > 9 then V := V2 + 55;
Num := Chr(Round(V)) + Num;
end;
If Length(Num) = 0 then Num := '0';
ConvertBase := Num;
End; {ConvertBase }
Function Caps(CapStr : AnyString) : AnyString; {Returns a string with
all characters, except those within quotes, converted to uppercase }
var
Quoted : boolean;
i : integer;
Begin {Caps }
Quoted := false;
for i := 1 to Length(CapStr) do
begin
if CapStr[i] = Quote then Quoted := NOT Quoted;
if NOT Quoted then CapStr[i] := UpCase(CapStr[i]);
end;
Caps := CapStr;
End; {Caps }
Procedure GetField; {Starting at LinePtr, trys to return next field in FldStr
sets Found if successful. (similar to NextWord) }
var
QuotedString : boolean;
Begin {GetField }
While (LinePtr <= EndPtr) AND (InpLine[LinePtr] in [' ',',']) Do
LinePtr := Succ(LinePtr); {strip unwanted chars }
If LinePtr > EndPtr then
begin
Found := false;
EXIT;
end;
If InpLine[LinePtr] = Quote then {Strings enclosed in quotes }
begin
Delete(InpLine,LinePtr,1);
StrgEnd := Pos(Quote,InpLine);
If StrgEnd <> 0 then StrgEnd := Succ(StrgEnd);
Insert(Quote,InpLine,LinePtr);
If StrgEnd > 0 then LinePtr2 := Succ(StrgEnd);
QuotedString := true;
end;
If NOT QuotedString then
begin
LinePtr2 := LinePtr;
While (LinePtr2 <= EndPtr) AND NOT (InpLine[LinePtr2] in [' ',',']) Do
LinePtr2 := Succ(LinePtr2);
end;
FldStr := Copy(InpLine,LinePtr,LinePtr2 - LinePtr);
LinePtr := LinePtr2;
Found := true;
End; {GetField }
Procedure ParseLine; {Parses InpLine for Label, OpStr, SourceStr, DestStr }
Begin {ParseLine }
LinePtr := 1;
LinePtr2 := 1;
LabelStr := '';
OpStr := '';
SourceStr := '';
DestStr := '';
EndPtr := Pos(';',InpLine) - 1; {Ignore comment after ";" }
If EndPtr = -1 then EndPtr := Length(InpLine);
If EndPtr = 0 then EXIT; {No source code on line }
InpLine := Caps(InpLine); {Convert to all CAPS, except quoted strings }
{Label? }
If InpLine[1] <> ' ' then
begin
GetField;
LabelStr := Copy(FldStr,1,25);
If LabelStr[Length(LabelStr)] = ':' then
Delete(LabelStr,Length(LabelStr),1);
end;
{OpCode? }
GetField;
If NOT Found then EXIT;
OpStr := FldStr;
{Save Ptr to start of operands }
OpdPtr := LinePtr;
{Destination operand, if any }
GetField;
If NOT Found then EXIT;
DestStr := FldStr;
{Source operand, if any }
GetField;
If Found then SourceStr := FldStr;
End; {ParseLine }
Procedure OperandLookup(OLSym : AnyString); {Look up OLSym in SymTable }
Begin {OperandLookup, really a Symbol Lookup, but...}
TablePtr := 1;
While (SymTable[TablePtr].Symbol <> OLSym) AND (TablePtr < NumSym) Do
TablePtr := Succ(TablePtr);
If SymTable[TablePtr].Symbol = OLSym then Found := true
Else Found := false;
End; {OperandLookup }
Procedure LookupOp; {Search for OpCode }
var
Move : Real;
Start : integer;
Begin {LookupOp }
{Use binary search to speed up process }
Move := NumOp;
Start := Round(Move/2);
While Move >= 2 Do
begin
Move := Move/2;
If OpStr > OpCodes[Start].Mnemonic Then Start := Start + Round(Move)
Else Start := Start - Round(Move);
If Start < 1 then Start := 1;
If Start > NumOp then Start := NumOp;
end;
OpPtr := Start;
Found := false;
While (OpPtr <= NumOp) AND NOT Found Do
With OpCodes[OpPtr] Do
begin
If Mnemonic > OpStr then Found := true; {Not really, but... }
If Mnemonic = OpStr then
If SrcType AND SType <> 0 then
If DstType AND DType <> 0 then Found := true;
If NOT Found then OpPtr := Succ(OpPtr);
end;
If OpCodes[OpPtr].Mnemonic <> OpStr then Found := false;{Fix earlier mistake}
End; {LookupOp }
Procedure NewEntry(NewSymbol : AnyString); {Add a symbol to SymTable }
Begin {NewEntry }
{Already in table? }
OperandLookup(NewSymbol);
If Found then
begin
ErrorMessage('Dup definition of '+NewSymbol);
EXIT;
end;
{Too many labels? }
If NumSym >= MAXSYM then
begin
ErrorMessage('Too many user symbols');
EXIT;
end;
{Make new entry }
NumSym := Succ(NumSym);
With SymTable[NumSym] Do
begin
Symbol := NewSymbol;
Val1 := Loctr;
SymType := NEAR;
end;
End; {NewEntry }
Procedure TestNumber(TNStr : AnyString); {Trys to interpret TNStr as a
number; may be in base ten, hex, or binary }
var
ValError : integer;
Begin {TestNumber }
Found := false;
{Hex? }
If TnStr[Length(TnStr)] = 'H' then
begin
Delete(TnStr,Length(TnStr),1);
Val('$'+TnStr,NumVal,ValError);
end
Else
{Binary?}
If TnStr[Length(TnStr)] = 'B' then
begin
Delete(TnStr,Length(TnStr),1);
TnStr := ConvertBase(TnStr,Binary,BaseTen);
Val(TnStr,NumVal,ValError);
end
Else
{Decimal?}
Val(TnStr,NumVal,ValError);
If ValError = 0 then
begin
Found := true;
If Length(Hex(NumVal)) < 3 then NumType := IMMED16 OR IMMED8
Else NumType := IMMED16;
end
End; {TestNumber }
Procedure MemRef(DataType : integer); {Builds memory address word }
Begin {MemRef }
If DataType = MEMY then DataType := DVal1 Else DataType := Sval1;
ObjLen := ObjLen + 2;
Obj[ObjLen-1] := Lo(DataType);
Obj[ObjLen] := Hi(DataType);
End; {MemRef }
Procedure MemoryRef(MemStr : AnyString); {Trys to interpret MemStr as direct
memory reference }
var
MR : AnyString;
Begin {MemoryRef }
If (MemStr[1] = '[') AND (MemStr[Length(MemStr)] = ']') then
begin
MemStr := Copy(MemStr,2,Length(MemStr) - 2);
TestNumber(MemStr);
If Found then
MemAddr := NumVal
Else
begin
OperandLookup(MemStr);
If Found then
If (SymTable[TablePtr].SymType AND IMMED16) <> 0 then
MemAddr := SymTable[TablePtr].Val1
Else
Found := false;
end
end
End; {MemoryRef }
Procedure ProcOffset(OS : AnyString); {interpret OS as an offset operand }
Begin {ProcOffset }
Found := true;
If Copy(OS,1,7) <> 'OFFSET(' then
Found := False
Else
begin
If Pass = 1 then
OffsetType := IMMED16
Else
begin
OS := Copy(OS,8,Length(OS) - 8);
OperandLookup(OS);
If Found AND (SymTable[TablePtr].SymType AND (MEMY OR NEAR) <> 0) then
begin
OffsetVal := SymTable[TablePtr].Val1;
OffsetType := IMMED16;
end
Else
begin
ErrorMessage('Illegal or Undefined arg. for Offset');
OffsetVal := 0;
Found := true;
OffsetType := IMMED16;
end
end
end
End; {ProcOffset }
Procedure ParseDispOffReg(PDOR : AnyString); {interpret PDOR as offset off
of a register }
var
RegStr : AnyString;
Pointer : integer;
Procedure ParseDisp(DispStr : AnyString);
Begin {internal ParseDisp }
DispStr := Copy(DispStr,1,Pointer - 1);
OperandLookup(DispStr);
If Found AND
(SymTable[TablePtr].SymType AND (IMMED16 OR IMMED8) <> 0) then
begin
NeedOffset := SymTable[TablePtr].SymType;
Offset := SymTable[TablePtr].Val1;
EXIT;
end;
TestNumber(DispStr);
If Found then
begin
NeedOffset := NumType;
Offset := NumVal;
EXIT;
end;
ProcOffset(DispStr);
If Found then
begin
NeedOffset := OffsetType;
Offset := OffsetVal;
end
end; {internal ParseDisp }
Begin {ParseDispOffReg }
If PDOR = '[BP]' then
begin
RegVal := 6;
NeedOffset := IMMED8;
Offset := 0;
Found := true;
end
Else
begin
Pointer := Pos('[',PDOR);
If Pointer <= 1 then
begin
Found := false;
EXIT;
end;
RegStr := Copy(PDOR,Pointer,Length(PDOR) - Pointer + 1);
If RegStr <> '[BP]' then
begin
OperandLookup(RegStr);
If NOT Found OR (SymTable[TablePtr].SymType <> MemReg) then
begin
Found := false;
EXIT;
end
Else
begin
RegVal := SymTable[TablePtr].Val1;
ParseDisp(RegStr);
end
end
Else
begin
RegVal := 6;
ParseDisp(RegStr);
end
end
End; {ParseDispOffReg }
Procedure Charactor(ch : AnyString); {checks to see if ch is quoted char }
Begin {Charactor }
Found := false;
If Length(ch) = 3 then
If ch[1] = Quote then
If ch[Length(ch)] = Quote then
begin
Found := true;
CharVal := Ord(ch[2]);
end;
End; {Charactor }
Procedure TypeOperand(OperStr : AnyString); {checks type of operand }
Begin {TypeOperand }
{Any operand? }
If Length(OperStr) = 0 then
begin
TargType := NONE;
EXIT;
end;
{In Symbol Table? }
OperandLookup(OperStr);
If Found then
begin
TargType := SymTable[TablePtr].SymType;
TargVal1 := SymTable[TablePtr].Val1;
If TablePtr <= Predef then TargVal2 := SymTable[TablePtr].Val2;
EXIT;
end;
{Number? }
TestNumber(OperStr);
If Found then
begin
TargType := NumType;
TargVal1 := NumVal;
EXIT;
end;
{Direct memory reference? }
MemoryRef(OperStr);
If Found then
begin
TargType := MEMY;
TargVal1 := MemAddr;
EXIT;
end;
{Offset off register? }
ParseDispOffReg(OperStr);
If Found then
begin
TargType := MEMREG;
TargVal1 := RegVal;
EXIT;
end;
{Offset? }
ProcOffset(OperStr);
If Found then
begin
TargType := OffSetType;
TargVal1 := OffsetVal;
EXIT;
end;
{Character? }
Charactor(OperStr);
If Found then
begin
TargType := IMMED8 OR IMMED16;
TargVal1 := CharVal;
EXIT;
end;
{String? }
If OperStr[1] = Quote then
begin
TargType := STRG;
EXIT;
end;
{Not found? Assume Near Label or Memory Reference, (error on Pass 2) }
If Pass = 2 then ErrorMessage('Undefined Symbol '+ OperStr);
TargType := NEAR OR MEMY;
End; {TypeOperand }
Procedure OpType; {Decides between word and byte operands }
Begin {OpType }
If ((DType OR SType) AND (REG16 OR ACUM16 OR SEGMNT OR C_S) <> 0) then
Word := true
Else
If ((DType OR SType) AND (REG8 OR ACUM8) <> 0) then
Word := false
Else
If OpStr[Length(OpStr)] = 'B' then
Word := false
Else
Word := true;
End; {OpType }
Procedure BuildOpCode; {builds the op code }
Begin {BuildOpCode }
ObjLen := Succ(ObjLen);
Obj[ObjLen] := OpCodes[OpPtr].OpCodeVal;
If (Flag AND ADDREG) <> 0 then
If (DType AND (SEGMNT OR C_S)) <> 0 then
Obj[ObjLen] := Obj[ObjLen] + DVal2
Else
If (Flag AND DIRECTION) <> 0 then
Obj[ObjLen] := Obj[ObjLen] + SVal2 DIV 8
Else
Obj[ObjLen] := Obj[ObjLen] + Dval2 DIV 8;
If ((Flag AND AUTOW) <> 0) AND Word then Obj[ObjLen] := Succ(Obj[ObjLen]);
If ((Flag AND AUTOC) <> 0) AND (SType AND CL <> 0) then
Obj[ObjLen] := Obj[ObjLen] + 2;
End; {BuildOpCode }
Procedure BuildModeByte; {builds addressing mode byte, and if necessary
the displacement byte(s) }
var
M : integer;
Begin {BuildModeByte }
ObjLen := Succ(ObjLen);
If ((DType OR SType) AND MEMY) <> 0 then
begin
If DType = MEMY then M := SVal2 else M := DVal2;
Obj[ObjLen] := 6 + M;
MemRef(DType);
EXIT;
end;
If (Flag AND DIRECTION) <> 0 then M := SVal1 + DVal2 Else M := DVal1 + SVal2;
Obj[ObjLen] := M;
If NeedOffset <> NONE then
begin
If (Offset <= 127) AND (Offset >= -128) then
begin
Obj[ObjLen] := Obj[ObjLen] + 64;
If Offset < 0 then Offset := Offset AND $FF;
ObjLen := Succ(ObjLen);
Obj[ObjLen] := Offset;
end
Else
begin
Obj[ObjLen] := Obj[ObjLen] + 128;
ObjLen := ObjLen + 2;
Obj[ObjLen-1] := Lo(Offset);
Obj[ObjLen] := Hi(Offset);
end;
end;
End; {BuildModeByte }
Procedure BuildExtensionByte; {builds the opcode extension from bits 3-5 of
the flag word }
Var
Ext,Mask : integer;
Begin {BuildExtensionByte }
Mask := $38;
Ext := Flag AND Mask;
If (Flag AND DIRECTION) <> 0 then DVal2 := Ext Else SVal2 := Ext;
BuildModeByte;
End; {BuildExtensionByte }
Procedure BuildDisp8; {calcs displacement from present location to
location given as operand }
var
D : integer;
Begin {BuildDisp8 }
D := DVal1 - Loctr;
If ABS(D) >= 128 then
begin
D := 0;
If Pass = 2 then ErrorMessage('Too far for short jump');
end;
If D < 0 then D := D AND $FF;
ObjLen := Succ(ObjLen);
Obj[ObjLen] := D;
End; {BuildDisp8 }
Procedure BuildDisp16; {calcs disp from loc to loc }
var
D : integer;
Begin {BuildDisp16 }
D := DVal1 - Loctr;
If (OpStr = 'JMP') AND (D <= 128) then DiagMessage('Could use JMPS');
If (D < 0) AND (OpStr <> 'CALL') then
begin
D := 0;
If Pass = 2 then ErrorMessage('Illegal reverse long jump');
end;
ObjLen := ObjLen + 2;
Obj[ObjLen-1] := Lo(D);
Obj[ObjLen] := Hi(D);
End; {BuildDisp16 }
Procedure BuildImmed8; {builds byte of immediate data }
var
IVal : integer;
Procedure SubImmed8;
Begin {internal SubImmed8 }
If NOT (IVal in [0..255]) then
begin
IVal := 0;
If Pass = 2 then ErrorMessage('Data too long');
end;
ObjLen := Succ(ObjLen);
Obj[ObjLen] := IVal;
End; {internal SubImmed8 }
Begin {BuildImmed8 }
If (DType AND IMMED8) <> 0 then
begin
IVal := DVal1;
SubImmed8;
end;
If (SType AND IMMED8) <> 0 then
begin
IVal := SVal1;
SubImmed8;
end;
End; {BuildImmed8 }
Procedure BuildImmed16; {builds immediate word(s) }
Begin {BuildImmed16 }
If (DType AND IMMED16) <> 0 then
begin
ObjLen := ObjLen + 2;
Obj[ObjLen-1] := Lo(DVal1);
Obj[ObjLen] := Hi(DVal1);
end;
If (SType AND IMMED16) <> 0 then
begin
ObjLen := ObjLen + 2;
Obj[ObjLen-1] := Lo(SVal1);
Obj[ObjLen] := Hi(SVal1);
end;
End; {BuildImmed16 }
Procedure ProcMachOp; {updates Loctr based on op length, and makes obj code }
Begin {ProcMachOp }
OpType;
Loctr := Succ(Loctr);
If Pass = 2 then BuildOpCode;
If (OpCodes[OpPtr].OpCodeVal = $D5) OR (OpCodes[OpPtr].OpCodeVal = $D4) then
begin
Loctr := Succ(Loctr);
If Pass = 2 then
begin
ObjLen := Succ(ObjLen);
Obj[ObjLen] := $A;
end;
end;
If NeedOffset <> NONE then
If (NeedOffset AND IMMED8 <> 0) then Loctr := Succ(Loctr)
Else Loctr := Loctr + 2;
If (Flag AND (NEEDMODEBYTE OR NEEDEXT)) <> 0 then
If ((DType OR SType) AND MEMY) <> 0 then Loctr := Loctr + 2;
If (Flag AND NEEDEXT) <> 0 then
begin
Loctr := Succ(Loctr);
If Pass = 2 then BuildExtensionByte;
end;
If (Flag AND NEEDMODEBYTE) <> 0 then
begin
Loctr := Succ(Loctr);
If Pass = 2 then BuildModeByte;
end;
If (Flag AND NEEDISP8) <> 0 then
begin
Loctr := Succ(Loctr);
If Pass = 2 then BuildDisp8;
end;
If (Flag AND NEEDISP16) <> 0 then
begin
Loctr := Loctr + 2;
If Pass = 2 then BuildDisp16;
end;
If (Flag AND NEEDIMMED8) <> 0 then
begin
Loctr := Succ(Loctr);
If Pass = 2 then BuildImmed8;
end;
If NOT Word AND ((Flag AND NEEDIMMED) <> 0) then
begin
Loctr := Succ(Loctr);
If Pass = 2 then BuildImmed8;
end;
If Word AND ((Flag AND NEEDIMMED) <> 0) then
begin
If DType = IMMED16 then Loctr := Loctr + 4 Else Loctr := Loctr + 2;
If Pass = 2 then BuildImmed16;
end;
If (Flag AND NEEDMEM) <> 0 then
begin
Loctr := Loctr + 2;
If Pass = 2 then MemRef(DType);
end;
End; {ProcMachOp }
Procedure ProcEQU; {EQU pseudo op }
Begin {ProcEQU }
If LabelStr = '' then
begin
If Pass = 2 then ErrorMessage('EQU without symbol');
end
Else
If Pass <> 2 then
begin
If DType = (NEAR OR MEMY) then
ErrorMessage('EQU with forward reference')
Else
begin
SymTable[NumSym].Val1 := DVal1;
SymTable[NumSym].SymType := DType;
end;
end;
End; {ProcEQU }
Procedure ProcORG; {ORG pseudo op }
Begin {ProcORG }
Loctr := DVal1;
End; {ProcORG }
Procedure ProcDB; {DB pseudo op }
Procedure BuildByte;
Begin {internal BuildByte }
ObjLen := Succ(ObjLen);
Obj[ObjLen] := NumVal;
End; {internal BuildByte }
Procedure BuildStg;
Begin {internal BuildStg }
FldStr := Copy(FldStr,2,Length(FldStr)-2);
For i := 1 to Length(FldStr) Do
begin
ObjLen := Succ(ObjLen);
Obj[ObjLen] := ord(FldStr[i]);
end;
End; {internal BuildStg }
Begin {ProcDB }
If LabelStr <> '' then SymTable[NumSym].SymType := MEMY;
LinePtr := OpdPtr;
LinePtr2 := OpdPtr;
While LinePtr < EndPtr Do
begin
GetField;
If NOT Found then
begin
Loctr := Loctr + ObjLen;
EXIT;
end;
TargetStr := FldStr;
TestNumber(TargetStr);
If Found AND (NumType AND IMMED8 <> 0) then
BuildByte
Else
If FldStr[1] = Quote then
BuildStg
Else
If Pass = 2 then ErrorMessage('Unrecognized operand '+FldStr);
end; {While..Do }
Loctr := Loctr + ObjLen;
End; {ProcDB }
Procedure ProcDS; {DS pseudo op }
var
DSVal : integer;
Begin {ProcDS }
DSFlag := true;
If LabelStr <> '' then SymTable[NumSym].SymType := MEMY;
If (SType AND IMMED8) <> 0 then DSVal := SVal1 Else DSVal := 0;
If Pass <> 1 then For i := 1 to DVal1 do Write(ObjFile,chr(DSVal));
Loctr := Loctr + DVal1;
End; {ProcDS }
Procedure ProcPROC; {PROC pseudo op }
Begin {ProcPROC }
If StkTop < MAXSTK then
begin
StkTop := Succ(StkTop);
ProcType[StkTop] := DType;
end
Else
If Pass <> 1 then ErrorMessage('Procedures nested too deeply');
End; {ProcPROC }
Procedure ProcENDP; {ENDP pseudo op }
Begin {ProcENDP }
If StkTop > 0 then StkTop := Pred(StkTop)
Else If Pass <> 1 then ErrorMessage('ENDP without PROC');
End; {ProcENDP }
Procedure PseudoOp;
Begin {PseudoOp }
Case OpCodes[OpPtr].OpCodeVal of
1 : ProcEQU;
2 : ProcORG;
3 : ProcDB;
4 : ProcDS;
5 : ProcPROC;
6 : ProcENDP;
End;
End; {PseudoOp }
Procedure UpdateLoctr; {decodes operation and advances Loctr }
Begin {UpdateLoctr }
TypeOperand(DestStr);
DType := TargType;
DVal1 := TargVal1;
DVal2 := TargVal2;
If OpStr = 'RET' then
SType := ProcType[StkTop]
Else
begin
TypeOperand(SourceStr);
SType := TargType;
SVal1 := TargVal1;
SVal2 := TargVal2;
end;
TargetStr := OpStr;
LookupOp;
If Found then
begin
Flag := OpCodes[OpPtr].Flagss;
If (Flag AND MACHOP) <> 0 then
ProcMachOp
Else
PseudoOp;
end
Else
If Pass <> 1 then
begin
ErrorMessage('Syntax Error: '+OpStr);
If ((ACUM8 OR ACUM16 OR REG8 OR REG16 OR SEGMNT OR C_S) AND
(DType OR SType)) = 0 Then
If (SType AND (NONE OR IMMED8 OR IMMED16)) <> 0 Then
If NOT (OpStr[Length(OpStr)] in ['B','W']) then
DiagMessage('Specify Word or Byte Operation');
end;
End; {UpdateLoctr }
Procedure Progress; {Gives user status of assemble }
var
X,Y : integer;
Begin {Progress }
If LineNum MOD 6 = 0 then {Only update every fourth line, saves time.. }
begin
LowVideo;
X := WhereX;
Y := WhereY;
GoToXY(60,1);
If Pass = 1 then Write('Pass: 1 Line: ',LineNum)
Else Write('Pass: 2 Line: ',LineNum);
ClrEol;
GoToXY(X,Y);
NormVideo;
end;
End; {Progress }
Procedure CheckPhase; {label value same on both passes? }
Begin {CheckPhase }
If OpStr <> 'EQU' then
begin
TargetStr := LabelStr;
OperandLookup(TargetStr);
With SymTable[TablePtr] Do
If ((SymType AND (NEAR OR MEMY)) <> 0) AND (Val1 <> Loctr) Then
ErrorMessage('Phase Error');
end;
End; {CheckPhase }
Procedure WrOutput; {Write the object code and then listing }
var
H : AnyString;
Spacing : byte;
Begin {WrOutput }
For i := 1 to ObjLen Do Write(ObjFile,Chr(Obj[i]));
CodeSize := CodeSize + ObjLen;
If ListLoc <> NoIO then
begin
If DSFlag then H := Hex(Loctr-DVal1) Else H := Hex(Loctr-ObjLen);
{Pad hex number }
Case Length(H) of
0 : H := '0000';
1 : H := '000' + H;
2 : H := '00' + H;
3 : H := '0' + H;
End; {Case }
Write(ListFile,H,' ');
Spacing := 0;
For i := 1 to ObjLen Do
begin
H := Hex(Obj[i]);
If Length(H) = 1 then H := '0' + H;
Write(ListFile,H);
Spacing := Spacing + Length(H);
end;
Writeln(ListFile,'':16-Spacing,LineNum:4,' ',InpLine);
end;
End; {WrOutput }
Procedure FirstPass; {Adds user-defined symbols to symbol table }
Begin {FirstPass }
Pass := 1;
Loctr := 256;
LineNum := 0;
EndofSource := false;
If SourceLoc = Memory then
begin {reset CurLine to point to first line in text stream }
With Curwin^ Do
begin
CurLine := TopLine;
While CurLine^.BackLink <> NIL Do
CurLine := CurLine^.BackLink;
end
end
Else
begin
Assign(SourceFile,SourceName);
Reset(SourceFile);
end;
While NOT EndOfSource Do
begin
GetLine;
ParseLine;
If LabelStr <> '' then NewEntry(LabelStr);
If OpStr <> '' then UpdateLoctr;
Progress;
end;
If SourceLoc = Disk then Close(SourceFile);
End; {FirstPass }
Procedure PassTwo; {Generates object code }
Begin {PassTwo }
Pass := 2;
Loctr := 256;
LineNum := 0;
EndofSource := false;
If SourceLoc = Memory then
begin {reset CurLine to point to first line in text stream }
With Curwin^ Do
begin
CurLine := TopLine;
While CurLine^.BackLink <> NIL Do
CurLine := CurLine^.BackLink;
end
end
Else
begin
Assign(SourceFile,SourceName);
Reset(SourceFile);
end;
While NOT EndOfSource Do
begin
GetLine;
If NOT EndOfSource then
begin
ParseLine;
If LabelStr <> '' then CheckPhase;
If OpStr <> '' then UpdateLoctr;
WrOutput;
Progress;
end;
end;
If SourceLoc = Disk then Close(SourceFile);
End; {PassTwo }
Procedure DumpSymTable; {show the symbol table }
Begin {DumpSymTable }
If ListLoc <> NoIO then
begin
Writeln(ListFile);
Writeln('Symbol Table Dump');
i := Predef + 1;
While i <= NumSym Do
With SymTable[i] Do
begin
Writeln(ListFile,Symbol:20,Hex(Val1):8);
i := Succ(i);
end;
end;
End; {DumpSymTable }
Procedure FinalProc; {we must always finish what we started }
Begin {FinalProc }
If StkTop > 0 then ErrorMessage('missing ENDP');
Writeln(ListFile);
Writeln(ListFile,Pred(LineNum),' Lines Assembled');
Writeln(ListFile,'CodeSize: ',CodeSize,' Bytes');
Writeln(ListFile,Errs,' Error(s) detected');
Writeln(ListFile,Diag,' Diagnostic(s) offered');
DumpSymTable;
Close(ObjFile);
If SourceLoc = Disk then Close(SourceFile);
If ListLoc = Disk then Close(ListFile);
End; {FinalProc }
Procedure SetUpTables; {Reads the file TChasm.Dat into the OpCode and
Symbol tables. }
Var
i : integer;
Begin {SetUpTables }
Repeat
If NOT Exist(OPCODEFILE) then
begin
Writeln('"',OPCODEFILE,'" is not on this disk.');
Writeln('[I]nsert new disk or [Q]uit? ');
Repeat Read(Kbd,ch) Until UpCase(ch) in ['I','Q'];
If UpCase(ch) = 'Q' then HALT;
end;
Until Exist(OPCODEFILE);
Assign(DataFile,OPCODEFILE);
Reset(DataFile);
Readln(DataFile,NumOp); {Read NumOp from file to see if too large }
if NumOp > MAXNUMOP then
begin
Writeln('Number of defined OpCodes is too large');
HALT;
end;
Readln(DataFile,InpLine); {Read Quote character and skip comments in file }
Quote := InpLine[1]; {get quote character }
For i := 1 to NumOp do
With OpCodes[i] Do
begin
Readln(DataFile,OpCodeVal,DstType,
SrcType,Flagss,InpLine);
{$V-}
NextWord(InpLine,Mnemonic,1,[' ',',']);
{$V+}
end;
Readln(DataFile,Predef); {Read Predef from file to see if it matches }
if Predef > MAXSYM then
begin
Writeln('Number of defined Symbols is too large');
HALT;
end;
Readln(DataFile,InpLine); {skip comments in file }
For i := 1 to Predef do
With SymTable[i] Do
begin
Readln(DataFile,Val1,Val2,SymType,InpLine);
{$V-}
NextWord(InpLine,Symbol,1,[' ',',']);
{$V+}
end;
NumSym := Predef;
Close(DataFile);
End; {SetUpTables }
Procedure TitleStart;
Begin {TitleStart }
{Print Title }
NormVideo;
ClrScr;
GoToXY(18,25);
Writeln('┌────────────────────────────────────────────┐');GoToXY(18,25);
Writeln('│ │');GoToXY(18,25);
Writeln('│ "Turbo" Cheap Assembler 1.0 │');GoToXY(18,25);
Writeln('│ by Mark Streich │');GoToXY(18,25);
Writeln('│ based upon CHASM (tm) │');GoToXY(18,25);
Writeln('│ by Dave Whitman │');GoToXY(18,25);
Writeln('│ │');GoToXY(18,25);
Writeln('│ │');GoToXY(18,25);
Writeln('└────────────────────────────────────────────┘');
Write(#10#10#10#10#10#10#10#10#10#10);
End; {TitleStart }
{********************** Main Screen Procedures *****************************}
Procedure ShowDirectory; {from Turbo Tutor (tm) - GREAT book and disk,
This is just one of many programs included }
type
Char12arr = array [ 1..12 ] of Char;
String20 = string[ 20 ];
RegRec =
record
AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags : Integer;
end;
var
Regs : RegRec;
DTA : array [ 1..43 ] of Byte;
Mask : Char12arr;
NamR : AnyString;
Drive,
Error, I : Integer;
begin {ShowDirectory }
FillChar(DTA,SizeOf(DTA),0); { Initialize the DTA buffer }
FillChar(Mask,SizeOf(Mask),0); { Initialize the mask }
FillChar(NamR,SizeOf(NamR),0); { Initialize the file name }
Write( 'Directory Mask? ' );
Readln(NamR);
NamR := Caps(NamR);
If NamR = '' then NamR := LogDir + '*.*'
Else
if (Length(NamR) = 2) AND (Pos(':',NamR) = 2) then NamR := NamR + '\*.*'
Else
if Length(NamR) = 1 then NamR := NamR + ':\*.*'
Else
if Pos(':',NamR) = 0 then NamR := LogDir + NamR;
Drive := Ord(NamR[1])-64;
Writeln('Directory of ',NamR);
For I := 1 to Length(NamR) Do Mask[I] := NamR[I];
Regs.AX := $1A00; { Function used to set the DTA }
Regs.DS := Seg(DTA); { store the parameter segment in DS }
Regs.DX := Ofs(DTA); { " " " offset in DX }
MSDos(Regs); { Set DTA location }
Error := 0;
Regs.AX := $4E00; { Get first directory entry }
Regs.DS := Seg(Mask); { Point to the file Mask }
Regs.DX := Ofs(Mask);
Regs.CX := 1; { Store the option }
MSDos(Regs); { Execute MSDos call }
Error := Regs.AX and $FF; { Get Error return }
I := 1; { initialize 'I' to the first element }
if (Error = 0) then
repeat
NamR[I] := Chr(Mem[Seg(DTA):Ofs(DTA)+29+I]);
I := I + 1;
until not (NamR[I-1] in [' '..'~']) or (I>20);
NamR[0] := Chr(I-1); { set string length because assigning }
{ by element does not set length }
while (Error = 0) do begin
Error := 0;
Regs.AX := $4F00; { Function used to get the next }
{ directory entry }
Regs.CX := 22; { Set the file option }
MSDos( Regs ); { Call MSDos }
Error := Regs.AX and $FF; { get the Error return }
I := 1;
repeat
NamR[I] := Chr(Mem[Seg(DTA):Ofs(DTA)+29+I]);
I := I + 1;
until not (NamR[I-1] in [' '..'~'] ) or (I > 20);
NamR[0] := Chr(I-1);
if (Error = 0)
then Write(NamR,'':16-Length(NamR));
end;
Writeln;
If Drive in [1..26] then
begin
Regs.AX := $3600; { Get Disk free space }
Regs.DX := Drive; { Store Drive number }
MSDos( Regs ); { Call MSDos to get disk info }
Writeln(((Regs.AX*Regs.CX*1.0)*Regs.BX):1:0,' Bytes Free');
end;
Write('>');
End; {ShowDirectory }
Procedure Say(S: AnyString); {Will write to the screen a string passed
as a parameter. Special control sequences can be embedded into the
string to set the Normal Video (%!) and Low Video and do a Carriage
Return (%@). For example, '%!Hello' will highlight the letter H and
write the rest of the word in normal video }
Var
I: Integer;
Begin
I:=1;
While I<=Length(S) Do
Begin
If Ord(S[I])<32 Then Write('^',Chr(Ord(S[I])+64))
Else If S[I]<>'%' Then Write(S[I])
Else If S[I+1]='@' Then {%@ = Carriage Return }
Begin
WriteLn;
LowVideo;
I:=I+1;
End
Else If S[I+1]='!' Then {%! = HighVideo for next char }
Begin
NormVideo;
Write(S[I+2]);
LowVideo;
I:=I+2;
End
Else If S[I+1]='#' Then {%# = Set HighVideo until unset}
Begin
NormVideo;
I := I+1;
End
Else Write('%');
I:=I+1;
End; { While I<=Length(S) }
NormVideo;
End; { Say }
Procedure MainScreen; {Handles the Main Control Screen }
Begin {MainScreen }
ClrScr;
LowVideo;
Say('Turbo Cheap Assembler%@%@');
GetDir(0,LogDir); { Get the current directory of the current drive. }
Say('%!Logged Directory: %#'+LogDir+'%@%@');
Say('%!Work File: %#'+SourceName+'%@');
Case SourceLoc of
Disk : Say('%@File Location: Disk%@');
Memory : Say('%@File Location: Memory%@');
End; {Case SourceLoc }
Case ListLoc of
Scrn : Say('%!Output Location: %#Screen%@%@');
Printer : Say('%!Output Location: %#Printer%@%@');
NoIO : Say('%!Output Location: %#None%@%@');
Else Say('%!Output Location: %#'+ListName+'%@%@');
End; {Case ListType }
Say('%!Edit %!Save%@');
Say('%!Directory %!Assemble %!Quit%@%@%!>');
End; {MainScreen }
Procedure ChangeDirectory;
Var
TempDir : AnyString;
Begin {ChangeDirectory }
TempDir := LogDir;
Repeat
Write('New Directory? ');
Readln(LogDir);
LogDir := Caps(LogDir);
if LogDir = '' then LogDir := TempDir
Else
if (LogDir='A') OR (LogDir='B') OR (LogDir='C') then
LogDir := LogDir + ':';
{$I-}
ChDir(LogDir);
{$I+}
Until IOresult = 0;
Writeln;
Write('>');
End; {ChangeDirectory }
Procedure ChangeWorkFile;
Var
TempWorkFile : AnyString;
Begin {ChangeWorkFile }
If SourceLoc = Memory then
begin
if EditChangeFlag then
begin
Write('Save changes? [Y/N] ');
Repeat Read(Kbd,ch) Until UpCase(ch) in ['Y','N'];
Writeln(UpCase(ch));
If UpCase(ch) = 'Y' then if EditFileWrite(SourceName) then;
EditChangeFlag := false; {reset for new file }
end;
EditWindowDeleteText;
end;
OK := true;
SourceLoc := Disk; {reset for new file }
Write('New Work File? ');
Readln(SourceName);
If SourceName <> '' then
begin
SourceName := Caps(SourceName);
If Pos('.',SourceName) = 0 then SourceName := SourceName + '.ASM';
If Exist(SourceName) then
begin
Write('Loading...');
CurWin^.FileName := SourceName;
EditReaTxtFil(SourceName);
If NOT OK then
begin
Write('Source too large - Will assemble from Disk');
EditWindowDeleteText;
end
Else
SourceLoc := Memory;
Writeln;
end
Else
begin
Writeln('New file');
SourceLoc := Memory;
Curwin^.FileName := SourceName;
end
end;
Write('>');
End; {ChangeWorkFile }
Procedure ChangeListLoc; {changes where output listing will go }
Begin {ChangeListLoc }
Write('Send Output to [P]rinter, [S]creen, [D]isk file, [CR]-None ');
Repeat Read(Kbd,ch) Until UpCase(ch) in ['P','S','D',#13 {CR} ];
Writeln(UpCase(ch));
Write('>');
Case UpCase(ch) of
'P' : ListLoc := Printer;
'S' : ListLoc := Scrn;
#13 : ListLoc := NoIO;
'D' : Begin
ListLoc := Disk;
Write('List File name? [.LST] ');
Readln(ListName);
ListName := Caps(ListName);
Write('>');
if Length(ListName) = 0 then
begin
If SourceName <> '' then
ListName := Copy(SourceName,1,Pos('.',SourceName)-1) + '.LST'
Else ListLoc := NoIO;
end
else
if Pos('.',ListName) = 0 then ListName := ListName + '.LST';
End;
End; {Case ch }
End; {ChangeListLoc }
Procedure Assemble; {starts the whole mess a goin' }
Begin {Assemble }
If SourceName <> '' then
begin
{Initialize Variables }
For i := 0 to MAXOBJ do Obj[i] := 0;
For i := 0 to MAXSTK do ProcType[i] := 0;
StkTop := 0;
Errs := 0;
Diag := 0;
CodeSize := 0;
NumSym := Predef;
{Init Object file }
ObjName := Copy(SourceName,1,Pos('.',SourceName)-1) + '.COM';
Assign(ObjFile,ObjName);
Rewrite(ObjFile);
{Init List file, if any }
Case ListLoc of
Scrn,
NoIO : Assign(ListFile,'TRM:');
Printer : Assign(ListFile,'LST:');
Disk : begin
Assign(ListFile,ListName);
Rewrite(ListFile);
end;
End; {Case }
FirstPass;
PassTwo;
FinalProc;
end
Else
Writeln('No Source File Specified');
Write('>');
End; {Assemble }
Procedure Quit;
Begin {Quit }
Quitting := true;
if SourceLoc = Memory then
begin
if EditChangeFlag then
begin
Write('Save changes? [Y/N] ');
Repeat Read(Kbd,ch) Until UpCase(ch) in ['Y','N'];
Writeln(UpCase(ch));
If UpCase(ch) = 'Y' then
begin
Write('Saving...');
if EditFileWrite(SourceName) then;
end;
end;
EditWindowDeleteText;
end
End; {Quit }
Procedure EditFile; { Calls the editor functions}
var
r,c : byte;
Begin {EditFile }
If (SourceName = '') OR (SourceLoc = Disk) then
begin
if SourceName = '' then Writeln('No Work file specified')
else Writeln('Cannot edit ',SourceName);
Write('>');
EXIT;
end;
{ Initialize screen array and other stuff }
for r := 1 to Defnorows do
for c := 1 to Defnocols do
with Screen [r,c] do
begin
Ch := chr (0); {Have the editor clean up the screen}
Color := Txtcolor
end;
RunDown := false;
EditWindowTopFile;
EditUpdPhyScr;
EditSystem;
MainScreen;
End; {EditFile }
Procedure SaveFile; {Calls the editor save function }
Begin {SaveFile }
If (SourceName = '') OR (SourceLoc = Disk) then
begin
if SourceLoc = Disk then Writeln('File not in memory')
else Writeln('No Work file specified');
Write('>');
EXIT;
end;
If Exist(SourceName) then
begin
Write('Overwrite existing file? [Y/N] ');
Repeat Read(Kbd,ch) Until UpCase(ch) in ['Y','N'];
If UpCase(ch) = 'N' then
begin
Writeln;
Write('>');
EXIT;
end
end;
Write('Saving...');
If EditFileWrite(SourceName) then;
Writeln;
Write('>');
End; {SaveFile }
BEGIN
TitleStart;
SetUpTables;
EditInitialize;
MainScreen;
While NOT Quitting Do
begin
Read(Kbd,ch);
Case UpCase(ch) of
'W' : ChangeWorkFile;
'S' : SaveFile;
'D' : ShowDirectory;
'E' : EditFile;
'A' : Assemble;
'O' : ChangeListLoc;
'Q' : Quit;
'L' : ChangeDirectory;
Else MainScreen;
End; {Case ch }
end;
END.